import applyMixin from './mixin';
import {forEach} from './util'
import ModuleCollection from './module/module-collection';
let Vue

function getState(store, path) {
  return path.reduce((newState, current) => {
    return newState[current]
  }, store.state)
}

function installModule(store, rootState, path, module) {
  // 注册事件时 需要注册到 对应都得命名空间中
  let namespace = store._modules.getNamespace(path)
  if(path.length > 0) {
    let parent = path.slice(0, -1).reduce((memo, current)=>{
      return memo[current]
    }, rootState)
    store._withCommitting(()=>{
      Vue.set(parent, path[path.length-1], module.state)
    })
  }

  module.forEachMutation((mutation, type)=>{
    // console.log(mutation, type)
    store._mutations[namespace+type] = store._mutations[namespace+type] || []
    store._mutations[namespace+type].push((payload) => {
      store._withCommitting(()=>{
        mutation.call(store, getState(store, path), payload)
      })
      store._subscribers.forEach(sub => sub({mutation, type}, store.state))
      
    })
  })
  module.forEachAction((action, type)=>{
    // console.log(action, type)
    store._actions[namespace+type] = store._actions[namespace+type] || []
    store._actions[namespace+type].push((payload) => {
      action.call(store, store, payload)
    })
  })
  module.forEachGetters((getter, type)=>{
    store._wrappedGetters[namespace+type] = function() {
      return getter(getState(store, path))
    }
  })

  module.forEachChild((child, key)=>{
    installModule(store, rootState, path.concat(key), child)
  })
}

function resetStoreVm(store, state) {
  const wrappedGetters = store._wrappedGetters
  let oldVm = store._vm
  let computed = {}
  store.getters = {}
  forEach(wrappedGetters, (fn, key)=>{
    computed[key] = function() {
      return fn()
    }
    Object.defineProperty(store.getters, key, {
      get: () => store._vm[key]
    })
  })
  store._vm = new Vue({
    data: {
      $$state: state
    },
    computed
  })
  if(store.strict) {
    // 会立即执行 同步跟新   在状态变化后 
    store._vm.$watch(()=>store._vm._data.$$state, ()=>{
      console.assert(store._committing, '在mutation之外发生了改变')
    }, {deep: true, sync: true})
  }
  if(oldVm) {
    console.log(oldVm)
    Vue.nextTick(()=>oldVm.$destroy())
  }
}

// 默认模块没有作用域  相同名字的mutation都执行
// 状态不要和模块的名字相同
// 默认计算属性 直接通过getters取值
class Store{
  constructor(options) {

    // 1收集树 格式化用户传入的参数  格式化成树形结构
    this._modules = new ModuleCollection(options)
    // 2安装 模块  将模块的属性定义在store 
    let state = this._modules.root.state  // 根状态

    this.strict = options.strict
    // 同步的watcher
    this._committing = false

    this._mutations = {}
    this._actions = {}
    this._wrappedGetters = {}
    this._subscribers = []

    installModule(this, state, [], this._modules.root)

    // 将状态放到Vue实例中
    resetStoreVm(this, state)

    // 插件
    options.plugins.forEach(plugin => plugin(this))

    // let state = options.state
    // // 
    // this.getters = {};
    // const computed = {}
    // forEach(options.getters, (fn, key)=>{
    //   computed[key] = () => {
    //     return fn(this.state)
    //   }
    //   Object.defineProperty(this.getters, key, {
    //     get: () => this._vm[key]

    //   })
    // })

    // 命名以$ 开头的属性 不会被代理到实例上 
    // this._vm = new Vue({
    //   data: {
    //     $$state: state
    //   },
    //   computed
    // })

    // this._mutations = {}
    // forEach(options.mutations, (fn, type) => {
    //   this._mutations[type] = (payload) => fn.call(this, this.state, payload)
    // })

    // this._actions = {}
    // forEach(options.actions, (fn, type) => {
    //   this._actions[type] = (payload) => fn.call(this, this, payload)
    // })
    
  }
  _withCommitting(fn) {
    let committing = this._committing
    this._committing = true
    fn()
    this._committing = false
  }
  subscribe(fn) {
    this._subscribers.push(fn)
  }

  commit = (type, payload) => {
    this._mutations[type].forEach(fn=>fn(payload))
  }
  dispatch = (type, payload) => {
    this._actions[type].forEach(fn=>fn(payload))
    // this._actions[type](payload)
  }

  registerModule(path, rawModule) {
    if(typeof path == 'string') path = [path]
    // 模块注册
    this._modules.register(path, rawModule)

    // 安装
    installModule(this, this.state, path, rawModule.newModule)
    // 现在这边计算属性还不行  还要做一般resetStoreVm
    resetStoreVm(this, this.state)
  }
  replaceState(newState) {
    store._withCommitting(()=>{
      this._vm._data.$$state = newState
    })
  }
  get state() {
    return this._vm._data.$$state
  }
}

const install = (vue) => {
  Vue = vue
  applyMixin(Vue)
}

export {
  Store,
  install
}